1
2
3 package net.sf.voruta;
4
5 import java.util.*;
6 import java.lang.reflect.*;
7 import java.lang.reflect.Array;
8 import java.sql.*;
9 import java.beans.*;
10 import java.io.*;
11
12
13
14
15 /***
16 *
17 * @author baliuka
18 */
19 final class SqlParser implements Language,LanguageFactory {
20
21 static final String ENDLN = System.getProperty("line.separator");
22
23 StringBuffer sb = new StringBuffer();
24
25 boolean escape;
26 char [] sql;
27
28 int indexMap[];
29 List fragments = new ArrayList();
30 int dynamicIndexMap[];
31 BeanProperties.Property propertyPaths[];
32
33 ClassFinder finder;
34 Method method;
35
36
37 SqlParser(){ }
38
39 private String getValue(String constName){
40
41 constName = constName.trim();
42
43 String value = Db.getProperty(constName);
44 if(value != null){
45 return value;
46 }
47 try{
48 int cln = constName.lastIndexOf('.');
49 if(cln == -1){
50
51 return method.getDeclaringClass().getField(constName.trim()).get(null).toString();
52
53 }else{
54
55 String fld = constName.substring(cln + 1,constName.length());
56 Object f = finder.findByName( constName.substring(0,cln) ).
57 getField( fld ).get(null);
58 if(ThreadLocal.class.isAssignableFrom(f.getClass())){
59 return ((ThreadLocal)f).get().toString();
60 } else{
61 return f.toString();
62 }
63 }
64
65 }catch(Exception e){
66 throw new DbParseException("can not evaluate constant " + constName,e);
67 }
68 }
69
70 private boolean isDigit(char c){
71 return escape&&Character.isDigit(c) ;
72 }
73
74 private boolean isBlank(char c){
75 return escape && ( c == '\t' || c == ' ' ||
76 c == '\n' || c == '\r' ||
77 c == ',' || c == ')' );
78 }
79
80 private boolean isConst(char c){
81 return escape && c == '{' ;
82 }
83
84
85
86 String generateSql(Object args[]){
87
88 StringBuffer buffer = new StringBuffer();
89 int index = 0;
90
91 for(Iterator i = fragments.iterator(); i.hasNext();){
92 buffer.append(i.next());
93 try{
94
95 buffer.append( args[dynamicIndexMap[index++]] );
96
97 }catch(ArrayIndexOutOfBoundsException ae){
98
99 throw new DbException( "dynamic sql parameter " + index + "(" + args.length
100 + ")" + " is out of range " ,ae);
101 }
102 }
103 return buffer.append(sb.toString()).toString();
104 }
105
106 private Object defaultValue( Class cls )throws Exception{
107
108 if( cls == null || cls == Object.class ){
109
110 return null;
111
112 }else if( java.util.Date.class.isAssignableFrom(cls) ){
113 return cls.getConstructor( new Class[]{ long.class } )
114 .newInstance( new Object[]{
115 new Long(System.currentTimeMillis())
116 });
117 }else if(cls.isPrimitive()){
118 if( int.class == cls ){
119 return new Integer(0);
120 }else if( long.class == cls ) {
121 return new Long(0);
122 }else if( float.class == cls ){
123 return new Float(0);
124 }else if( double.class == cls ){
125 return new Double(0);
126 }else if( char.class == cls ){
127 return new Character('\u0000');
128 }else if( boolean.class == cls ){
129 return Boolean.FALSE;
130 }else{
131 return new Integer(0);
132 }
133
134 }else{
135 //let driver to convert
136 return new Integer(0);
137 }
138 }
139
140 private void setDefaultParams(PreparedStatement ps)throws Exception{
141 Class types[] = method.getParameterTypes();
142 for(int i = 0; i < indexMap.length; i++ ){
143
144 if( propertyPaths[ i ] != null ){
145 ps.setObject(1 + i , defaultValue( propertyPaths[i].getType() ));
146 }else{
147
148 ps.setObject(1 + i , defaultValue( types[indexMap[i]] ));
149 }
150
151 }
152
153 }
154
155 Object[] prepare(Object args[])throws Exception{
156
157 int len = indexMap.length;
158 Object pargs[] = new Object[ len ];
159
160 for(int i = 0; i < len; i++ ){
161
162 try{
163
164 Object arg ;
165 if(indexMap[i] != -1 ){
166 arg = args[ indexMap[i] ];
167 }else{
168 arg = null;
169 }
170
171 if( propertyPaths[ i ] != null ){
172
173 pargs[i] = propertyPaths[i].getValue(arg);
174
175 }else{
176
177 pargs[i] = arg;
178
179 }
180
181 }catch(ArrayIndexOutOfBoundsException ae){
182
183 throw new DbException( generateSql(args) + " parameter " + i +" is out of range (" + indexMap[i] + ")" ,ae);
184 }
185 }
186
187 return pargs;
188 }
189
190 private void parse(){
191
192 List indexes = new java.util.ArrayList();
193 List dynamicIndexes = new java.util.ArrayList();
194 List paths = new java.util.ArrayList();
195 int i = 0;
196
197 try{
198
199 LOOP:
200 for ( ; i < sql.length; i++ ){
201
202 if(sql[i] == '$'){
203 if( escape ){
204 sb.append('$');
205 escape = false;
206 continue LOOP;
207 }
208 escape = true;
209 continue LOOP;
210 }
211 try{
212
213 if( isConst(sql[i]) ){
214 for( int j = i ; j < sql.length ; j++ ){
215 if(sql[j] == '}'){
216 int cnt = j - i;
217 String str = new String(sql,i + 1,cnt - 1);
218 try{
219 dynamicIndexes.add( new Integer( Integer.parseInt(str) - 1 ) );
220 fragments.add(sb.toString());
221 sb.delete(0,i);
222 }catch(NumberFormatException nfe){
223 sb.append(getValue(str));
224 }
225 i += cnt;
226 continue LOOP;
227 }
228 }
229 }
230
231
232 if ( escape ){
233
234 if( isDigit(sql[i]) ){
235 for( int j = i; j <= sql.length; j++ ){
236 if(j == sql.length || !isDigit(sql[j]) ){
237 int cnt = j - i;
238 Integer p = new Integer(Integer.parseInt(new String(sql,i,cnt)) - 1 );
239 indexes.add( p );
240 sb.append('?');
241 i += cnt - 1 ;
242 if( i < sql.length - 1 && sql[i + 1] == '.'){
243 i++;
244 for( j = i ; j < sql.length && !isBlank(sql[j]); j++ );
245 cnt = j - i - 1;
246 String pName = new String(sql,i + 1,cnt);
247 BeanProperties.Property prop =
248 BeanProperties.getProperty(method.getParameterTypes()[p.intValue()],pName );
249 paths.add( prop );
250 i += cnt ;
251
252 }else{
253 paths.add(null);
254 }
255 continue LOOP;
256 }
257 }
258 }else{ //ThreadLocal
259
260 for( int j = i; j <= sql.length; j++ ){
261 if(j == sql.length || sql[j] ==' ' || sql[j]=='\n' ){
262 int cnt = j - i;
263 String name = new String(sql,i,cnt);
264 int ind = name.lastIndexOf('.');
265 Class declarer;
266 String fieldName;
267 if(ind == -1 ){
268 declarer = method.getDeclaringClass();
269 fieldName = name;
270 }else{
271 declarer = finder.findByName( name.substring(0,ind) );
272 fieldName = name.substring(ind);
273 }
274 indexes.add( new Integer(-1) );
275 sb.append('?');
276 paths.add(BeanProperties.getProperty(declarer,fieldName));
277 i += cnt ;
278 continue LOOP;
279 }
280 }
281 throw new DbParseException("ThreadLocal parameter expected");
282 }
283
284 }//end escape
285
286 sb.append(sql[i]);
287
288 }finally{
289
290 escape = false;
291
292 }
293
294 }
295 }catch(Exception e){
296 char blanks[] = new char[ i + 1 ];
297 Arrays.fill(blanks,' ');
298 blanks[ i ] = '^';
299 StringBuffer msg = new StringBuffer( ENDLN );
300 msg.append( sql );
301 msg.append( ENDLN );
302 msg.append( blanks );
303 throw new DbParseException(msg.toString(),e);
304 }
305
306
307 propertyPaths = (BeanProperties.Property [])
308 paths.toArray( new BeanProperties.Property[]{} );
309
310 dynamicIndexMap = new int[dynamicIndexes.size()];
311
312 for( int j = 0; j < dynamicIndexMap.length; j++ ){
313 dynamicIndexMap[j] = ((Integer)dynamicIndexes.get(j)).intValue();
314 }
315
316 indexMap = new int[indexes.size()];
317 for( int j = 0; j < indexMap.length; j++ ){
318 indexMap[j] = ((Integer)indexes.get(j)).intValue();
319 }
320
321 }
322
323 public Object executeQuery(String connection, Object[] args,
324 ResultSetHandler handler, Object[] userObj)
325 throws Exception{
326
327 return DbUtils.executeQuery( connection, generateSql(args) ,
328 prepare(args), handler,method.getReturnType(), args);
329 }
330
331 public int executeUpdate(String connection, Object[] args) throws Exception{
332
333 return DbUtils.executeUpdate(connection, generateSql(args), prepare(args));
334 }
335
336 /*** voruta calls it once to prepare/compile query string
337 *
338 */
339 public void compile(ClassFinder finder, Method method,
340 String query, Properties tags) throws Exception{
341
342 this.sql = query.toCharArray();
343 this.method = method;
344 this.finder = finder;
345
346
347 parse();
348
349 }
350
351 public Language newInstance() {
352 return new SqlParser();
353 }
354
355 private boolean canExplain(){
356 String s = sb.toString().trim().toUpperCase();
357 if( s.length() == 0 || dynamicIndexMap.length > 0){
358 return false;
359 }
360 if( s.startsWith("SELECT") ||
361 s.startsWith("UPDATE") ||
362 s.startsWith("DELETE") || s.startsWith("INSERT") ){
363 return true;
364 }
365 return false;
366 }
367
368 public void explain(String connection, java.io.PrintWriter pw) throws Exception {
369
370 if(!canExplain()){
371 return;
372 }
373
374 Connection c = ThreadLocalConnection.get(connection);
375
376 if(c.getWarnings() != null){
377 DbUtils.getLog().warn(c.getWarnings());
378 }
379
380 String query = Db.getExplainPrefix() + " " + sb.toString();
381 PreparedStatement ps = c.prepareStatement(query);
382 try{
383
384 setDefaultParams(ps);
385
386 StringBuffer buffer = new StringBuffer();
387 try{
388
389 buffer.append( method );
390 buffer.append('\n');
391 buffer.append( query );
392
393 if( ps.execute() ){
394
395 ResultSet rs = ps.getResultSet();
396 try{
397
398
399 ResultSetMetaData md = rs.getMetaData();
400 buffer.append('\n');
401 for(int i = 1; i <= md.getColumnCount(); i++ ){
402 buffer.append(md.getColumnName(i));
403 buffer.append("\t");
404 }
405 buffer.append('\n');
406 while(rs.next()){
407 for(int i = 1; i <= md.getColumnCount(); i++ ){
408 buffer.append(rs.getObject(i));
409 buffer.append("\t");
410 }
411 buffer.append('\n');
412
413 }
414
415
416 }finally{
417 rs.close();
418 }
419
420 }
421 }finally{
422 pw.print(buffer.toString());
423 }
424
425 SQLWarning warn = c.getWarnings();
426
427 if(warn != null){
428
429 pw.print(warn.getMessage());
430
431
432 };
433
434 c.clearWarnings();
435
436 }finally{
437 ps.close();
438 }
439
440 }
441
442 public void explain(String connection) throws Exception {
443
444 StringWriter sw = new StringWriter();
445 PrintWriter pw = new PrintWriter(sw);
446 explain(connection,pw);
447 pw.flush();
448 DbUtils.getLog().info(sw.getBuffer().toString());
449
450 }
451
452
453
454
455
456 }
457
458
459
460
This page was automatically generated by Maven